# Copyright (c) 2005,2006,2007 Dynamic Solutions Inc. (support@dynamic-solutions.com)
#
# This file is part of DataZoomer.
#
# DataZoomer is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# DataZoomer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Implementation of Interface Controls"""


from zoom import zoomer
from dzresponse import HTMLResponse, RedirectResponse, PDFResponse, ImageResponse
import qml2, dzhtml, types
from re import findall, compile

HANDLE_ARGS_RE = compile(r'(\w+)[=]{1}(?:["\'](.+?)["\']|(\S+))')
HANDLE_TAGS_RE = compile("<#(?P<tag>.+?)>")

class Control:
    """Base class for all Control classes
    Control objects render themselves by calling the theme object.  They
    don't know anything about HTML.  They use the primitives provided by
    the theme object to create more complex interface structures.  Every
    control needs to know how to render itself by calling upon the theme
    primitives.
    """
    qml = qml2.qml()
    def __init__(self):
        self.visible = 1
        self.enabled = 1
    def render(self):
        return zoomer.theme.render(self)
    def __str__(self): return self.render()

class Composite(Control,list):
    def __init__(self,content=[]):
        Control.__init__
        for item in content:
            self.append(item)
    def accept(self,visitor,result):
        for item in self: item.accept(visitor,result)

class Html(Control):
    def __init__(self,content=''):
        self.content = content
    def accept(self,visitor,result):
        visitor.visit_html(self,result)

class Tagged(Html):
    def __init__(self,content='',obj=None):
        Html.__init__(self,content)
        # This is the place to add more system tags
        self.DZ      = zoomer.main_url
        self.ROOT    = zoomer.root_url
        self.SID     = zoomer.sid
        self.APP     = zoomer.app.name
        self.object  = obj
    def accept(self,visitor,result):
        visitor.visit_tagged(self,result)

class Wiki(Tagged):
    def __init__(self,content='',obj=None):
        Tagged.__init__(self,content,obj)
    def accept(self,visitor,result):
        visitor.visit_wiki(self,result)

class Page(Control):
    def __init__(self,title='New Page',subtitle='',content=''):
        self.title    = title
        self.subtitle = subtitle
        self.content  = content
        self.icon     = zoomer.app.name
        self.message  = ''
        self.warning  = ''
        self.actions  = []
        self.views    = []
        self.parts    = Composite()
        self.theme    = zoomer.theme
        self.printerfriendly = 0
        self.rss = 0
        self.breadcrumb = 0
        self.footer = None
        self.controller = None

    def accept(self,visitor,result):
        visitor.visit_page(self,result)
    def render(self):
        return HTMLResponse(self.theme.render(self))

class UnformattedPage(Control):
    def __init__(self,content=''):
        self.content  = content
    def accept(self,visitor,result):
        result.append(self.content)
    def render(self):
        return HTMLResponse(zoomer.theme.render(self))

class ErrorPage(Page):
    def __init__(self,message='An error has occurred'):
        Page.__init__(self,'Error','An application error has occured',message)

class Redirect(Control):
    def __init__(self,url='?'):
        self.url = url
    def render(self):
        return RedirectResponse(self.url)

class Image(Control):
    def __init__(self,imagename,themename):
        self.image = imagename
        self.theme = themename
    def render(self):
        return ImageResponse(self.image,self.theme)

class PDF(Control):
    def __init__(self,document):
        self.document = document
    def render(self):
        return PDFResponse(self.document)

class File(Control):
    def __init__(self,filename):
        self.document = filename
    def render(self):
        from dzresponse import FileResponse
        return FileResponse(self.document)

class EmailBody(Control):
    def __init__(self,message):
        self.message = message
    def accept(self,visitor,result):
        visitor.visit_email_body(self,result)

class TaggedObject(Html):
    def __init__(self,content,obj):
        Html.__init__(self,content)
        self.DZ      = zoomer.main_url
        self.ROOT    = zoomer.root_url
        self.SID     = zoomer.sid
        self.APP     = zoomer.app.name
        self.obj     = obj
    def accept(self,visitor,result):
        visitor.visit_taggedobject(self,result)

class TaggedView(Tagged):
    include = "<#INCLUDE name=%s>"
    tag = "<#%s>"

    def __init__(self,content,view):
        self.content = content
        self.view = view
        Tagged.__init__(self,self.content)

    def __handle_args(self,arg_string):
        args = arg_string[arg_string.find(' ')+1:]
        ndic = {}
        for match in HANDLE_ARGS_RE.findall(args):
            if len(match)==3:
                ndic[match[0]] = (match[1] and match[1] or match[2])
        return ndic

    def handle_tags(self):
        """for each tag found
                if tag name is method of the view then use it,
                otherwise grab it from the tags dictionary
        """
        done=[]
        for match in HANDLE_TAGS_RE.findall(self.content):
            if match not in done and match[:7].upper()<>'INCLUDE':
                if match.lower() in self.view.__class__.__dict__:
                    fn = 'self.view.%s'%match.lower()
                    inc = TaggedView(str(eval(fn)()),self.view)
                    self.content = self.content.replace(self.tag%match,inc.render())
                elif match.find(' ')>=0:
                    fnname = match[:match.find(' ')].lower()
                    if fnname in self.view.__class__.__dict__:
                        args = self.__handle_args(match)
                        fn = 'self.view.%s'%fnname
                        inc = TaggedView(str(eval(fn)(args)),self.view)
                        self.content = self.content.replace(self.tag%match,inc.render())
                    else:
                        inc = TaggedView('[%s]'%(match[:match.find(' ')]),self.view)
                        self.content = self.content.replace(self.tag%match,inc.render())
                elif match in self.view._tags and not match in self.__dict__.keys():
                    inc = TaggedView(self.view._tags[match],self.view)
                    self.content = self.content.replace(self.tag%match,inc.render())
                done.append(match)

    def accept(self,visitor,result):
        self.handle_tags()
        visitor.visit_taggedview(self,result)

#===========================================================
# NOTE: EVERYTHING FROM HERE ON DOWN IS PRETTY ROUGH

class Tag(Control):
    def __init__(self,tag_type,content=None,**attributes):
        self.tag_type = tag_type
        self.content = content
        self.attributes = attributes
    def accept(self,visitor,result):
        return visitor.visit_tag(self,result)

class Browser(Control):
    def __init__(self,dataset,fields=[],title='',key_field='ID',on_click='show',labels={}):
        Control.__init__(self)
        self.dataset    = dataset
        self.title      = title
        self.key_field  = key_field
        self.on_click   = on_click
        self.fields     = fields
        self.labels     = labels
        self.on_delete  = None
    def accept(self,visitor,result):
        visitor.visit_browser(self,result)

class Action(Control):
    def __init__(self,caption,method='',**attributes):
        self.caption = caption
        self.attributes = attributes
        if method:
            self.method = method
        else:
            self.method = self.caption.replace(' ','_').lower()
    def accept(self,visitor,result):
        visitor.visit_action(self,result)

class Menu(Action):
    def accept(self,visitor,result):
        visitor.visit_menu(self,result)

class View(Action):
    def accept(self,visitor,result):
        visitor.visit_view(self,result)

class Panel(Control):
    def __init__(self,title='',content=''):
        self.title = title
        self.content = content
    def accept(self,visitor,result):
        visitor.visit_panel(self,result)

class SidePanel(Control):
    def __init__(self,title='',content=''):
        self.title = title
        self.content = content
    def accept(self,visitor,result):
        visitor.visit_sidepanel(self,result)

class Form(Control):
    def __init__(self,action='accept',content=''):
        self.content = content
        self.con = zoomer.webvars.con
        self.action = action
    def add(self,control):
        self.content.append(control)
    def accept(self,visitor,result):
        visitor.visit_form(self,result)

class TableView(Control):
    def __init__(self,tablename,columns=[]):
        Control.__init__(self)
        self.tablename = tablename
        self.columns   = columns
        self.new_button = Button('')
        self.sort_by   = ''
        self.key_field = ''
        self.on_click  = ''
        self.title     = ''
    def accept(self,visitor,result):
        if not self.title:
            self.title = self.tablename
        if self.sort_by:
            self.table = zoomer.db('select * from %s order by %s' % (self.tablename,self.sort_by))
        else:
            self.table = zoomer.db.table(self.tablename)
        visitor.visit_tableview(self,result)

class RecordView(Control):
    def __init__(self,record,labels={}):
        Control.__init__(self)
        self.record = record
        self.fields = FieldList()
        self.fields.load(record)
        self.labels = labels
    def drop(self,droplist):
        self.fields = self.fields.drop(droplist)
    def accept(self,visitor,result):
        visitor.visit_recordview(self,result)

class RecordEdit(Control):
    def __init__(self,record):
        Control.__init__(self)
        self.record = record
        self.fields = FieldList()
        self.fields.load(record)
        self.form = Form()
    def drop(self,droplist):
        self.fields = self.fields.drop(droplist)
    def accept(self,visitor,result):
        visitor.visit_recordedit(self,result)

class NewRecordEditor(Control):
    def __init__(self,table,method=''):
        Control.__init__(self)
        self.table = table
        self.fields = FieldList()
        self.fields.load(table.columns)
        self.form = Form(method)
        self.save_button = Button('Save')
        self.cancel_button = Button('Cancel')
        self._map=None
    def drop(self,droplist):
        self.fields = self.fields.drop(droplist)
    def accept(self,visitor,result):
        self.form.content = Composite()
        rec_edit = RecordEdit(self.table.columns)
        if self._map: rec_edit._map = self._map
        rec_edit.fields= self.fields
        self.form.content.append(rec_edit)
        self.form.content.append(self.save_button)
        self.form.content.append(self.cancel_button)
        self.form.content.append(Hidden('data_op','i'))
        self.form.accept(visitor,result)

#-----------------------
class aPage(Control):
    def __init__(self,title='',subtitle='',content=''):
        self.page_title = ''
        self.title      = title
        self.subtitle   = subtitle
        self.content    = content
        self.message    = '&nbsp;'
        self.leftpanel  = ''
        self.rightpanel = ''

    def render(self):
        def build_system_link(app):
            if app.name == zoomer.app.name:
                return zoomer.theme.selected_system_menu_link(app)
            else:
                return zoomer.theme.system_menu_link(app)

        def build_system_menu():
            top_app_names = [app.name for app in zoomer.user_apps if app.visible and app.system_app and app.name in zoomer.user_groups]
            if zoomer.authenticated:
                top_app_names.remove('login')
            else:
                if 'logout' in top_app_names:
                    top_app_names.remove('logout')
            separator = zoomer.theme.system_menu_separator()
            return separator.join([build_system_link(zoomer.apps[name]) for name in top_app_names])

        def build_menu_link(app):
            if app.name == zoomer.app.name:
                return zoomer.theme.selected_menu_link(app)
            else:
                return zoomer.theme.menu_link(app)

        def build_main_menu():
            top_app_names = [app.name for app in zoomer.user_apps if app.visible and not app.system_app and app.name in zoomer.user_groups]
            separator = zoomer.theme.menu_separator()
            return separator.join([build_menu_link(zoomer.apps[name]) for name in top_app_names])

        def build_view_menu():
            separator = zoomer.theme.view_menu_separator()
            return separator.join(zoomer.app.get_view_list())

        content = TaggedContentView(zoomer.theme.page_template())
        content.TITLE      = self.title
        content.SUBTITLE   = self.subtitle
        content.ICON       = zoomer.theme.icon(zoomer.app.name)
        content.PAGETITLE  = zoomer.name
        content.SYSTEMMENU = build_system_menu()
        content.MAINMENU   = build_main_menu()
        content.VIEWLINKS  = build_view_menu()

        content.MESSAGE    = zoomer.theme.message(self.message)

        if content.VIEWLINKS:
            content.LEFTPANEL = '<#VIEWLINKS>'
        else:
            content.LEFTPANEL = ''

        if content.LEFTPANEL:
            content.MAINPANEL = zoomer.theme.two_panel_template()
        else:
            content.MAINPANEL = zoomer.theme.one_panel_template()
        content.PAGECONTENT = self.content

        rendered_page_content = content.render()

        return HTMLResponse(rendered_page_content)


class NewRecordView(Control):
    def __init__(self,tablename,key_field):
        Control.__init__(self)
        self.tablename = tablename
        self.table = '<table>'
        self.key_field = key_field
        self.save_button = Button('')
        self.cancel_button = Button('')
        self.button = '<input type="submit" name=%s value=%s>'
        self.hidden = '<input type="hidden" name=%s value=%s>'
        self.text = '<input type="text" name="%s" value="%s" size=%s maxlength=%s>'

    def render(self):
        query = zoomer.db('select * from %s '% \
                 (self.tablename))
        result = self.table
        result += '<form method=post>'
        for column in query.cols:
            result += '<tr>'
            result += '<td>'+str.upper(column.name)+'</td>'
            result += '<td>'
            if str.upper(column.name)!=str.upper(self.key_field):
               result += self.text % (column.name," ",'25',column.size)
            result += '</td>'
            result += '</tr>'
        result += '</table>'
        result += self.hidden % ('data_op','i')
        result += self.hidden % ('sid',zoomer.sid)
        result += self.hidden % ('app',zoomer.app.name)
        if len(self.save_button.on_click):
           result += self.button % ('action',self.save_button.on_click)
        if len(self.cancel_button.on_click):
           result += self.button % ('action',self.cancel_button.on_click)
        result += '</form>'
        return result

class RecordEditView(Control):
    def __init__(self,tablename,key_field,key,data_errors={},record_errors={}):
        Control.__init__(self)
        self.data_errors = data_errors
        self.record_errors = record_errors
        self.tablename = tablename
        self.key_field = key_field
        self.key = key
        self.table = '<table>'
        self.save_button = Button('')
        self.cancel_button = Button('')
        self.button = '<input type="submit" name=%s value=%s>'
        self.hidden = '<input type="hidden" name=%s value=%s>'
        self.text = '<input type="text" name="%s" value="%s" size=%s maxlength=%s>'

    def render(self):
        query = zoomer.db('select * from %s where %s=%s' % \
                 (self.tablename,self.key_field,self.key))
        result = ''
        result += self.get_errors()
        result += '<form method=post>'
        colsdict = {}
        for col in query.cols:
            colsdict[str.upper(col.name)]=col.size
        if len(query):
           record = query[0]
           result += self.table
           for column in record:
               zoomer.debug(column)
               result += '<tr>'
               result += '<td>'+column[0]+'</td>'
               result += '<td>'
               if str.upper(column[0])==str.upper(self.key_field):
                  result += str(self.key)
               else:
                  zoomer.debug(column[0])
                  zoomer.debug(str(column[1]))
                  zoomer.debug(colsdict[column[0]])
                  result += self.text % (str.capitalize(column[0]),str(column[1]),'25',colsdict[column[0]])
               result += '</td>'
               result += '</tr>'
           result += '</table>'
        result += self.hidden % ('sid',zoomer.sid)
        result += self.hidden % ('app',zoomer.app.name)
        result += self.hidden % ('key',self.key)
        result += self.hidden % ('data_op','u')
        result += self.hidden % (self.key_field,self.key)
        if len(self.save_button.on_click):
           result += self.button % ('action',self.save_button.on_click)
        if len(self.cancel_button.on_click):
           result += self.button % ('action',self.cancel_button.on_click)
        result += '</form>'
        return result

    def get_errors(self):
        result = '<font color=red>'
        for item in self.data_errors:
            result += item + ': ' + self.data_errors[item]
            result += '<BR>'
        for item in self.record_errors:
            result += item + ': ' + self.record_errors[item]
            result += '<BR>'
        return result+'<BR></font>'


class Field:
    def __init__(self,obj,type='CHAR',size=30,label=None,maxlength=None,value=None,visible=1,readOnly=0,select={},flag='',default=None):
        self.visible = visible
        if isinstance(obj,str):
            self.name = obj
            self.type = type
            self.label = label
            self.size = size
            self.maxlength = maxlength
            self.value = value
            self.readOnly = readOnly
            self.select = select
            self.flag = flag
            self.default = default
        else:
            self.name      = obj.name
            self.type      = obj.type
            self.size      = obj.size
            self.label     = obj.label
            self.maxlength = obj.size
            self.value     = ''

    def __repr__(self):
        return repr(self.__dict__)


class FieldList(list):
    def keep(self,field_names):
        keep = []
        for field in self:
            if field.name in field_names:
                keep += [field]
        return FieldList(keep)

    def drop(self,field_names):
        drop = []
        for field in self:
            if field.name not in field_names:
                drop += [field]
        return FieldList(drop)

    def names(self):
        names = []
        for field in self:
            names += [field.name]
        return names

    def __getattr__(self,field_name):
        for field in self:
            if field.name == field_name:
                return field
        return None

    def __init__(self,obj=None):
        if obj:
            self.load(obj)

    def load(self,obj):
        import dzdb
        if isinstance(obj,list) or isinstance(obj,dzdb.Column):
            for field in range(len(self)):
                del self[0]
            for col in obj:
                if isinstance(col,Field):
                    self.append(col)
                else:
                    self.append(Field(col))

        elif isinstance(obj,dzdb.Record):
            for field in range(len(self)):
                del self[0]
            for col in obj._columns_:
                field = Field(col)
                field.value = obj.value_by_name(col.name)
                self.append(field)


class Input(Control):
    def __init__(self,field=None):
        self.field = field
        self.title = ''
    def __getitem__(self,name):
        return getattr(self.field,name)

class TextInput(Input):
    def accept(self,visitor,result):
        visitor.visit_textinput(self,result)

class MemoInput(Input):
    def accept(self,visitor,result):
        visitor.visit_memoinput(self,result)

class DateInput(Input):
    def accept(self,visitor,result):
        visitor.visit_dateinput(self,result)

class NumericInput(Input):
    def accept(self,visitor,result):
        visitor.visit_numericinput(self,result)

class HiddenInput(Input):
    def accept(self,visitor,result):
        visitor.visit_hidden(self,result)

class Hidden(Input):
   def __init__(self,name,value):
       self.name = name
       self.value = value
   def accept(self,visitor,result):
       visitor.visit_hidden(self,result)

class Radio(Input):
   def __init__(self,name,value):
       self.name = name
       self.value = value
   def accept(self,visitor,result):
       visitor.visit_radio(self,result)

class Checkbox(Radio):
   def __init__(self,name,value):
       self.name = name
       self.value = value
       self.checked=''

   def accept(self,visitor,result):
       visitor.visit_checkbox(self,result)

class Button(Control):
    def __init__(self,caption='',name=''):
        self.caption = caption
        self.name = name
    def accept(self,visitor,result):
        visitor.visit_button(self,result)
    def __getitem__(self,name):
        if name=='name' and not self.name:
            return self.caption.lower().replace(' ','_') + '_button'
        return self.__dict__[name]

class TextArea(Control):
    def __init__(self,name='',value=''):
        self.name  = name
        self.value = value
    def accept(self,visitor,result):
        visitor.visit_textarea(self,result)
    def __getitem__(self,name):
        return self.__dict__[name]

class ResetButton(Button):
    def accept(self,visitor,result):
        visitor.visit_resetbutton(self,result)

class ResetInput(ResetButton):
    pass

class Pulldown(Control):
    def __init__(self,name,itemlist,selected=None,all=0,label=''):
        self.name = name
        self.itemlist = itemlist
        self.selected = selected
        self.all = all
        self.label = label
    def accept(self,visitor,result):
        visitor.visit_pulldown(self,result)


if __name__ == '__main__':
    import unittest, dztheme, dzdb

    class MyApp:
        def __init__(self):
            self.name = 'test'
        def run(self):
            return HTMLResult('Hello World')

    class simple_theme:
        def visit_textinput(self,control,result):
            result.append('<input type=text name="%(name)s">' % control)

        def visit_memoinput(self,control,result):
            result.append('<textarea name="%(name)s"></textarea>' % control)

        def visit_dateinput(self,control,result):
            result.append('<input type=text name="%(name)s" size=8 maxlength=8>' % control)

        def visit_numericinput(self,control,result):
            result.append('<input type=text name="%(name)s" size=%(size)d maxlength=%(maxlength)d>' % control)

        def visit_hidden(self,control,result):
            result.append('<input type=hidden name="%(name)s" value="%(value)s">' % control)

        def visit_button(self,control,result):
            result.append('<input type=submit name="%(name)s" value="%(caption)s">' % control)

        def visit_form(self,control,result):
            tpl = '<form action="%(action)s" method=POST>%(content)s</form>'
            result.append(tpl % control.__dict__)

        def visit_recordview(self,control,result):
            content = Composite()
            for field in control.fields:
                content.append(Html('<tr><td>%s</td><tr><td>%s</td><tr>' % (field.name,field.value)))
            control.content = content
            tpl = '<table>%(content)s</table>'
            result.append(tpl % control.__dict__)

        def visit_html(self,control,result):
            result.append(control.content)

        def render(self,control):
            result = []
            control.accept(self,result)
            return ''.join(result)

    zoomer.app = MyApp()
    zoomer.theme = simple_theme()

    def get_test_database():
        from dzdb import Database
        import MySQLdb
        db = Database(MySQLdb.Connect,host='localhost',user='testuser',passwd='password',db='test')
        db('drop table if exists dzdb_test_table')
        db('create table dzdb_test_table (ID CHAR(10),AMOUNT NUMERIC(10,2),DTADD DATE,NOTES TEXT)')
        db('insert into dzdb_test_table values ("1234",50,"2005-01-14","Hello there")')
        db('insert into dzdb_test_table values ("5678",60,"2035-01-24","New notes")')
        return db

    class ViewTest(unittest.TestCase):

        def test_FieldList(self):
            field1 = Field('Field1','Char',20,'Field 1',20,'field 1 data',default='')
            field2 = Field('Field2','Memo',50,'Field 2',30,'field 2 data',1,0,{},'Error')
            field3 = Field('Field3','Pulldown',20,'Field 3',20,'a',1,0,{'A':'a','B':'b','C':'c'})
            field4 = Field('Field4','Checkbox',20,'Field 4',20,'yes',1,1)
            fieldlist = FieldList([field1,field2,field3,field4])
            dropfields = fieldlist.drop(['Field2'])
            self.assertEquals(len(dropfields),3)
            self.assertEquals(dropfields.names(),['Field1','Field3','Field4'])
            keepfields = fieldlist.keep(['Field1','Field2','Field4'])
            self.assertEquals(len(keepfields),3)
            self.assertEquals(fieldlist[0].size,20)
            self.assertEquals(fieldlist[2].type,'Pulldown')
            self.assertEquals(fieldlist[1].value,'field 2 data')
            self.assertEquals(fieldlist[3].readOnly,1)
            self.assertEquals(fieldlist.names(),['Field1','Field2','Field3','Field4'])

        def test_Field_constructor(self):
            field0 = Field('ID','CHAR',15,0)
            self.assertEquals(field0.name,'ID')
            self.assertEquals(field0.type,'CHAR')
            self.assertEquals(field0.size,15)
            col = dzdb.Column('ID','CHAR',15,0,2)
            field1 = Field(col)
            self.assertEquals(field1.name,'ID')
            self.assertEquals(field1.type,'CHAR')
            self.assertEquals(field1.size,15)

        def test_FieldList_load(self):
            col1 = dzdb.Column('ID','CHAR',15,0,2)
            col2 = dzdb.Column('NOTES','MEMO')
            col3 = dzdb.Column('DTADD','DATE')
            col4 = dzdb.Column('AGE','NUMERIC',10)
            col5 = dzdb.Column('AMOUNT','NUMERIC',10,2)
            columns = dzdb.Columns([col1,col2,col3,col4,col5])
            fields = FieldList()

            db = get_test_database()

            rs = db('select * from dzdb_test_table')
            fields.load(rs[0])

        def test_RecordView(self):
            db = get_test_database()
            rec = db('select * from dzdb_test_table')[0]
            rv = RecordView(rec)
            self.assertEquals(rv.render(),'')

        def test_Input(self):
            col1 = dzdb.Column('ID','CHAR',15,0,2)
            col2 = dzdb.Column('NOTES','MEMO')
            col3 = dzdb.Column('DTADD','DATE')
            col4 = dzdb.Column('AGE','NUMERIC',10)
            col5 = dzdb.Column('AMOUNT','NUMERIC',10,2)

            control = TextInput(Field(col1))
            self.assertEquals(control.render(),'<input type=text name="ID">')

            control = MemoInput(Field(col2))
            self.assertEquals(control.render(),'<textarea name="NOTES"></textarea>')

            control = DateInput(Field(col3))
            self.assertEquals(control.render(),'<input type=text name="DTADD" size=8 maxlength=8>')

            control = NumericInput(Field(col4))
            self.assertEquals(control.render(),'<input type=text name="AGE" size=10 maxlength=10>')

            control = NumericInput(Field(col5))
            self.assertEquals(control.render(),'<input type=text name="AMOUNT" size=10 maxlength=10>')

            control = HiddenInput(Field(col5))
            self.assertEquals(control.render(),'<input type=hidden name="AMOUNT" value="">')

        def test_Form(self):
            col1 = dzdb.Column('ID','CHAR',15,0,2)
            col2 = dzdb.Column('NOTES','MEMO')
            col3 = dzdb.Column('DTADD','DATE')
            col4 = dzdb.Column('AGE','NUMERIC',10)
            col5 = dzdb.Column('AMOUNT','NUMERIC',10,2)
            cols = dzdb.Columns([col1,col2,col3,col4,col5])

            form = Form('accept','test')
            self.assertEquals(form.render(),'<form action="accept" method=POST>test</form>')

            field_list = Composite()
            for col in cols.keep(['ID','NOTES']):
                field_list.append(TextInput(Field(col)))
            form.content = field_list

            target = '<form action="accept" method=POST><input type=text name="ID"><input type=text name="NOTES"></form>'
            self.assertEquals(form.render(),target)

        def test_Button(self):
            control = Button('Edit')
            self.assertEquals(control.render(),'<input type=submit name="edit_button" value="Edit">')
            control = Button('Edit','mybutton')
            self.assertEquals(control.render(),'<input type=submit name="mybutton" value="Edit">')

        def test_HtmlView(self):
            view = HtmlView()
            self.assertEquals(view.render(),'Text')
            view = HtmlView('test123')
            self.assertEquals(view.render(),'test123')

        def test_TextView(self):
            view = TextView()
            self.assertEquals(view.render(),'Text')
            view = TextView('test123')
            self.assertEquals(view.render(),'test123')
            view = TextView('!Hello')
            self.assertEquals(view.render(),'<H1>Hello</H1>')

        def test_Composite(self):
            view = Composite()
            view.append(Html('Line1'))
            view.append(Html('Line2'))
            self.assertEquals(view.render(),'Line1Line2')

        def test_TaggedContentView(self):
            view = TaggedContentView('test')
            self.assertEquals(view.render(),'test')

            view = TaggedContentView('te<#MISSING>st')
            self.assertEquals(view.render(),'te[MISSING]st')

            view = TaggedContentView('te<#VALUE>st')
            view.VALUE = 'mpe'
            self.assertEquals(view.render(),'tempest')

            view.content = '<#VALUE>st'
            self.assertEquals(view.render(),'mpest')

            view.content = 'te<#VALUE>'
            self.assertEquals(view.render(),'tempe')

            view.content = 'sid=<#SID>'
            self.assertEquals(view.render(),'sid=!')

            view.content = 'img=<#IMG>'
            self.assertEquals(view.render(),'img=/images/dz1/')

        def test_WikiView(self):
            view = WikiView('test')
            self.assertEquals(view.render(),'test')

            view.content = '!Hello\nThis is a test'
            self.assertEquals(view.render(),'<H1>Hello</H1>This is a test')

            view.USERNAME = 'Andy'
            view.content = '!Hello <#USERNAME>\nThis is a test'
            self.assertEquals(view.render(),'<H1>Hello Andy</H1>This is a test')

            view.content = 'Image url is: <#IMG>'
            self.assertEquals(view.render(),'Image url is: /images/dz1/')

        def test_PageView(self):
            view = Page('Hello <#APP>','Hello App','Testing Hello')

            self.assertEquals(view.title,'Hello <#APP>')
            self.assertEquals(view.subtitle,'Hello App')
            self.assertEquals(view.content,'Testing Hello')


    unittest.main()
